home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Chat & Communication / Digsby build 37 / digsby_setup.exe / lib / plugins / nowplaying / winamp.pyo (.txt) < prev    next >
Python Compiled Bytecode  |  2008-10-13  |  11KB  |  408 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. __all__ = [
  5.     'WinampSongChecker']
  6. import sys
  7. from util.ffi import cimport, Struct
  8. from ctypes.wintypes import DWORD, MAX_PATH
  9. from ctypes import byref, create_string_buffer, create_unicode_buffer, WinError, c_int, c_char_p, addressof, c_char, c_void_p
  10. from path import path
  11. from traceback import print_exc
  12. from nowplaying import SongChecker, register
  13. import logging
  14. log = logging.getLogger('nowplaying.winamp')
  15.  
  16. class WinampSongChecker(SongChecker):
  17.     PROCESS_NAME = 'winamp.exe'
  18.     app_name = 'Winamp'
  19.     name_id = 'winamp'
  20.     CLASS_NAME = u'Winamp v1.x'
  21.     important_keys = ('title', 'artist')
  22.     
  23.     def __init__(self):
  24.         SongChecker.__init__(self)
  25.         self.hwnd = None
  26.         self.hProcess = None
  27.         self.WinampData = None
  28.         self.methods = [
  29.             self._currentSongFromMetadata,
  30.             self._currentSongFromPlaylistTitle,
  31.             self._currentSongFromAppTitle]
  32.  
  33.     
  34.     def running(self, processes = None):
  35.         
  36.         try:
  37.             self.get_instance()
  38.         except:
  39.             self.release()
  40.             return False
  41.  
  42.         return True
  43.  
  44.     
  45.     def WASend(self, *a):
  46.         return SendMessageW(self.hwnd, WM_WA_IPC, *a)
  47.  
  48.     
  49.     def WAWrite(self, what, towhere):
  50.         if type(what) is str:
  51.             buf = create_string_buffer(what)
  52.         else:
  53.             buf = what
  54.         outsize = c_int()
  55.         success = WriteProcessMemory(self.hProcess, towhere._ptr, addressof(buf), len(buf), byref(outsize))
  56.         if not success:
  57.             raise WinError()
  58.         
  59.         if len(buf) != outsize.value:
  60.             raise Exception("Didn't write enough data (wrote %d, should have written %d)", len(buf), outsize.value)
  61.         
  62.  
  63.     
  64.     def WARead(self, address, bufsize = 512):
  65.         if not self.hProcess:
  66.             return None
  67.         
  68.         string = create_string_buffer(bufsize)
  69.         if not ReadProcessMemory(self.hProcess, address, byref(string), bufsize, 0):
  70.             raise WinError()
  71.         
  72.         return string.value
  73.  
  74.     
  75.     def get_metadata(self, filename, metaname):
  76.         if not self.hProcess:
  77.             return None
  78.         
  79.         metaname = metaname.upper()
  80.         WinampData = self.WinampData
  81.         WAWrite = self.WAWrite
  82.         WASend = self.WASend
  83.         WARead = self.WARead
  84.         efiRemote = None
  85.         fileNameRemote = None
  86.         fieldRemote = None
  87.         resultRemote = None
  88.         efiRemote = WinampData(extendedFileInfoStruct())
  89.         fileNameRemote = WinampData(len(filename) + 1)
  90.         fieldRemote = WinampData(len(metaname) + 1)
  91.         resultRemote = WinampData(512)
  92.         WAWrite(filename, fileNameRemote)
  93.         WAWrite(metaname, fieldRemote)
  94.         WAWrite(extendedFileInfoStruct(filename = fileNameRemote._ptr, metadata = fieldRemote._ptr, ret = resultRemote._ptr, retlen = 512), efiRemote)
  95.         if not WASend(efiRemote._ptr, IPC.GET_EXTENDED_FILE_INFO):
  96.             err = WinError()
  97.             if err.winerror != 0:
  98.                 raise err
  99.             
  100.         
  101.         result = WARead(resultRemote._ptr)
  102.         return result
  103.  
  104.     
  105.     def _currentSong(self):
  106.         if not self.hProcess:
  107.             return None
  108.         
  109.         WinampData = self.WinampData
  110.         WAWrite = self.WAWrite
  111.         WASend = self.WASend
  112.         WARead = self.WARead
  113.         (filename, position) = self.get_current_filename()
  114.         info = dict(status = isplaying.get(WASend(1, IPC.ISPLAYING), 'stopped'), length = WASend(1, IPC.GETOUTPUTTIME), playlist_position = position)
  115.         extrainfo = { }
  116.         errors = { }
  117.         print_errors = False
  118.         for method in self.methods[:]:
  119.             
  120.             try:
  121.                 moreinfo = method(filename, position)
  122.                 for k, v in moreinfo.iteritems():
  123.                     if v:
  124.                         extrainfo[k] = v
  125.                         continue
  126.             except Exception:
  127.                 e = None
  128.                 errors[method] = e
  129.                 del e
  130.  
  131.             if (all,)((lambda .0: for key in .0:
  132. key in extrainfo)(self.important_keys)):
  133.                 break
  134.                 continue
  135.         else:
  136.             print_errors = True
  137.         if (sys.DEV or print_errors) and errors:
  138.             for method, error in errors.items():
  139.                 log.info('Exception calling %r: %r', method, error)
  140.             
  141.             del method
  142.             del error
  143.         
  144.         del errors
  145.         if not extrainfo:
  146.             return None
  147.         
  148.         if filename:
  149.             filepath = path(filename.decode(sys.getfilesystemencoding()))
  150.             extrainfo.update(filepath = unicode(filepath), filename = filepath.namebase)
  151.         
  152.         info.update(extrainfo)
  153.         return info
  154.  
  155.     
  156.     def _currentSongFromAppTitle(self, filename = None, position = None):
  157.         if not self.hProcess:
  158.             return { }
  159.         
  160.         
  161.         try:
  162.             title = fix_title(GetWindowText(self.hwnd)).decode('fuzzy utf8')
  163.         except WindowsError:
  164.             e = None
  165.             if e.winerror == 1400:
  166.                 self._release()
  167.                 return { }
  168.             else:
  169.                 raise e
  170.         except:
  171.             e.winerror == 1400
  172.  
  173.         if re.match('Winamp [0-9]\\.[0-9]+', title):
  174.             return { }
  175.         else:
  176.             
  177.             try:
  178.                 (artist, title) = title.split(' - ', 1)
  179.             except ValueError:
  180.                 return { }
  181.  
  182.             return dict(artist = artist, title = title)
  183.  
  184.     
  185.     def _currentSongFromPlaylistTitle(self, filename = None, position = None):
  186.         if not filename and position:
  187.             (filename, position) = self.get_current_filename()
  188.         
  189.         
  190.         try:
  191.             playlist_title = self.WARead(self.WASend(position, IPC.GETPLAYLISTTITLE)).decode('fuzzy utf8')
  192.             if not playlist_title:
  193.                 raise Exception("Couldn't get playlist title")
  194.         except Exception:
  195.             e = None
  196.             return { }
  197.  
  198.         
  199.         try:
  200.             (artist, title) = playlist_title.split(' - ', 1)
  201.         except ValueError:
  202.             return { }
  203.  
  204.         return dict(artist = artist, title = title)
  205.  
  206.     
  207.     def _currentSongFromMetadata(self, filename = None, position = None):
  208.         if not self.hProcess:
  209.             return { }
  210.         
  211.         if not filename and position:
  212.             (filename, position) = self.get_current_filename()
  213.             if filename is None:
  214.                 return { }
  215.             
  216.         
  217.         metas = 'title artist genre album comment length year'.split()
  218.         metainfo = { }
  219.         for meta in metas:
  220.             
  221.             try:
  222.                 metainfo[meta] = self.get_metadata(filename, meta).decode('fuzzy utf8')
  223.             continue
  224.             except Exception:
  225.                 e = None
  226.                 if sys.DEV:
  227.                     print_exc()
  228.                 
  229.                 return { }
  230.                 continue
  231.             
  232.  
  233.         
  234.         return metainfo
  235.  
  236.     
  237.     def get_current_filename(self):
  238.         WASend = self.WASend
  239.         WARead = self.WARead
  240.         filename = None
  241.         position = WASend(0, IPC.GETLISTPOS)
  242.         playlist_length = WASend(0, IPC.GETLISTLENGTH)
  243.         if playlist_length:
  244.             filename = WARead(WASend(position, IPC.GETPLAYLISTFILE))
  245.         
  246.         return (filename, position)
  247.  
  248.     
  249.     def get_instance(self):
  250.         if self.hwnd:
  251.             return None
  252.         
  253.         hwnd = FindWindowW(self.CLASS_NAME, None)
  254.         if not hwnd:
  255.             return self.release()
  256.         
  257.         self.hwnd = hwnd
  258.         processId = DWORD()
  259.         GetWindowThreadProcessId(hwnd, byref(processId))
  260.         hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, 0, processId)
  261.         if not hProcess:
  262.             pass
  263.         hProcess = None
  264.         if not hProcess:
  265.             self.release()
  266.         else:
  267.             self.hProcess = hProcess
  268.             self.WinampData = MakeIPCDatatype(hProcess)
  269.  
  270.     
  271.     def release(self):
  272.         if self.hProcess is not None:
  273.             CloseHandle(self.hProcess)
  274.             self.hwnd = None
  275.             self.hProcess = None
  276.             self.WinampData = None
  277.         
  278.  
  279.  
  280. register(WinampSongChecker)
  281. import re
  282. num_matcher = re.compile('^([0-9]+\\. )')
  283.  
  284. def fix_title(s):
  285.     idx = s.find(' - Winamp')
  286.     if idx >= 0:
  287.         s = s[:idx]
  288.     
  289.     match = num_matcher.match(s)
  290.     if match:
  291.         s = s[len(match.group()):]
  292.     
  293.     return s
  294.  
  295. gwt_buffer = create_unicode_buffer(256)
  296.  
  297. def GetWindowText(hwnd):
  298.     if not GetWindowTextW(hwnd, byref(gwt_buffer), 256):
  299.         raise WinError()
  300.     
  301.     return gwt_buffer.value
  302.  
  303. cimport(user32 = [
  304.     'SendMessageW',
  305.     'FindWindowW',
  306.     'GetWindowTextW',
  307.     'GetWindowThreadProcessId'], kernel32 = [
  308.     'ReadProcessMemory',
  309.     'WriteProcessMemory',
  310.     'VirtualAllocEx',
  311.     'VirtualFreeEx',
  312.     'OpenProcess',
  313.     'CloseHandle'])
  314.  
  315. def MakeIPCDatatype(hProcess):
  316.     
  317.     class IPCData((object,)):
  318.         HPROCESS = hProcess
  319.         
  320.         def __init__(self, sz, datatype = None):
  321.             if type(sz) is int:
  322.                 if datatype is None:
  323.                     datatype = c_char * sz()
  324.                 
  325.             else:
  326.                 datatype = sz
  327.                 sz = len(datatype)
  328.             self._sz = sz
  329.             self._ptr = VirtualAllocEx(self.HPROCESS, None, sz, MEM_COMMIT, PAGE_READWRITE)
  330.             if not self._ptr:
  331.                 print >>sys.stderr, 'the following WindowsError occurred when callingVirtualAllocEx(%s, None, %s, MEM_COMMIT, PAGE_READWRITE)' % (self.HPROCESS, sz)
  332.                 raise WinError()
  333.             
  334.             self._value = type(datatype).from_address(self._ptr)
  335.  
  336.         
  337.         def free(self):
  338.             if self._ptr is not None:
  339.                 if not VirtualFreeEx(self.HPROCESS, self._ptr, 0, MEM_RELEASE):
  340.                     raise WinError()
  341.                 
  342.                 self._ptr = None
  343.             
  344.  
  345.         __del__ = free
  346.  
  347.     return IPCData
  348.  
  349.  
  350. class extendedFileInfoStruct(Struct):
  351.     _fields_ = [
  352.         ('filename', c_char_p),
  353.         ('metadata', c_char_p),
  354.         ('ret', c_char_p),
  355.         ('retlen', c_int)]
  356.  
  357. WM_WA_IPC = 1024
  358. PROCESS_ALL_ACCESS = 0
  359. PROCESS_VM_OPERATION = 8
  360. PROCESS_VM_READ = 16
  361. PROCESS_VM_WRITE = 32
  362. MEM_COMMIT = 4096
  363. MEM_DECOMMIT = 16384
  364. MEM_RELEASE = 32768
  365. PAGE_READWRITE = 4
  366.  
  367. class IPC:
  368.     GETVERSION = 0
  369.     ISPLAYING = 104
  370.     GETOUTPUTTIME = 105
  371.     GETLISTLENGTH = 124
  372.     GETLISTPOS = 125
  373.     GETINFO = 126
  374.     GETPLAYLISTFILE = 211
  375.     GETPLAYLISTTITLE = 212
  376.     GET_EXTENDED_FILE_INFO = 290
  377.     GET_EXTENDED_FILE_INFO_HOOKABLE = 296
  378.  
  379. isplaying = {
  380.     0: 'stopped',
  381.     1: 'playing',
  382.     3: 'paused' }
  383.  
  384. def process_string_at(hProcess, address):
  385.     bufsize = 512
  386.     string = create_string_buffer('\x00' * bufsize)
  387.     if not ReadProcessMemory(hProcess, address, byref(string), bufsize, 0):
  388.         raise WinError()
  389.     
  390.     return string.value
  391.  
  392. if __name__ == '__main__':
  393.     logging.basicConfig()
  394.     from time import clock
  395.     before = clock()
  396.     checker = WinampSongChecker()
  397.     checker.get_instance()
  398.     print GetWindowText(checker.hwnd)
  399.     for i in range(1):
  400.         print 'apptitle %r' % checker._currentSongFromAppTitle()
  401.         print 'playlist %r' % checker._currentSongFromPlaylistTitle()
  402.         print 'metadata %r' % checker._currentSongFromMetadata()
  403.     
  404.     checker.release()
  405.     duration = clock() - before
  406.     print 'duration', duration
  407.  
  408.